"""
主题: 防止死锁的加锁机制
问题: 你正在写一个多线程程序，其中线程需要一次获取多个锁，此时如何避免死锁问题。
提示 : 
"""

import threading
from contextlib import contextmanager

# Thread-local state to stored information on locks already acquired
_local = threading.local()

@contextmanager
def acquire(*locks):
    '''每一个锁分配一个唯一的id，然后只允许按照升序规则来使用多个锁'''
    # Sort locks by object identifier
    locks = sorted(locks, key=lambda x: id(x))

    # Make sure lock order of previously acquired locks is not violated
    acquired = getattr(_local,'acquired',[])
    if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):
        raise RuntimeError('Lock Order Violation')

    # Acquire all of the locks
    acquired.extend(locks)
    _local.acquired = acquired

    try:
        for lock in locks:
            lock.acquire()
        yield
    finally:
        # Release locks in reverse order of acquisition
        for lock in reversed(locks):
            lock.release()
        del acquired[-len(locks):]

def recipe1():
    x_lock = threading.Lock()
    y_lock = threading.Lock()

    def thread_1():
        while True:
            with acquire(x_lock, y_lock):
                print('Thread-1')

    def thread_2():
        while True:
            with acquire(y_lock, x_lock):
                print('Thread-2')

    t1 = threading.Thread(target=thread_1)
    t1.daemon = True
    t1.start()

    t2 = threading.Thread(target=thread_2)
    t2.daemon = True
    t2.start()
    
def recipe2():
    '''必定会有一个线程发生崩溃'''
    x_lock = threading.Lock()
    y_lock = threading.Lock()

    def thread_1():

        while True:
            with acquire(x_lock):
                with acquire(y_lock):
                    print('Thread-1')

    def thread_2():
        while True:
            with acquire(y_lock):
                with acquire(x_lock):
                    print('Thread-2')

    t1 = threading.Thread(target=thread_1)
    t1.daemon = True
    t1.start()

    t2 = threading.Thread(target=thread_2)
    t2.daemon = True
    t2.start()

def main():
    print('recipe1'.center(20, '*'))
    recipe1()

if __name__ == '__main__':
    main()          